
try:
	from __main__ import APPNAME, APPVER
except ImportError:
	APPNAME, APPVER = '<unknown>', '?'
from datetime import datetime
TIMESTAMP = lambda: datetime.today().strftime(' Saved on %A, %d %B %Y, %H:%M ')

__all__ = ['Document', 'Item']

if 0:

	from lxml.etree import ElementTree, XMLParser, SubElement, Comment, \
						   Entity, CDATA, LXML_VERSION, LIBXML_VERSION
	VERSION = ' %s version %s, using lxml %d.%d.%d, libxml2 %d.%d.%d ' % \
				((APPNAME, APPVER) + LXML_VERSION[:3] + LIBXML_VERSION[:3])

	def _add_tstmp(tree):
		# It seems that lxml won't serialize prolog items that are before
		# the doctype declaration, and there doesn't appear to be any way
		# work around this bug.
		tstmp = TIMESTAMP()
		root = tree.getroot()
		for elem in root.itersiblings(preceding=True):
			if 'saved' in elem.text.lower():
				elem.text = tstmp
				elem = elem.getnext()
				break
		else:
			root.addprevious(Comment(tstmp))
			root.addprevious(Comment(VERSION))
		if 'version' in elem.text.lower():
			elem.text = VERSION
		else:
			elem.addprevious(Comment(VERSION))

#except ImportError:
else:

	from etcompat import ElementTree, XMLParser, SubElement, Comment, \
						 Entity, CDATA, ET_VERSION, EXPAT_VERSION

	VERSION = ' %s version %s, using %s, %s ' % \
				(APPNAME, APPVER, ET_VERSION, EXPAT_VERSION)

#	def _add_tstmp(tree):
#		tstmp = TIMESTAMP()
#		docelem = tree.docinfo.docelem
#		for idx, elem in reversed(list(enumerate(docelem))):
#			if 'saved' in elem.text.lower():
#				elem.text = tstmp
#				break
#		else:
#			docelem.insert(0, 

#		for elem in tree.docinfo.docelem:
#			if 'saved' in elem.text.lower():
#				elem.text = tstmp
##				elem = elem.getnext()
#				break

#		if len(self._prolog) > 0 and 'saved' in self._prolog[0].text.lower():
#			self._prolog[0].text = tstmp
#		else:
#			self._prolog.insert(0, _Comment(tstmp))
#		if len(self._prolog) > 1 and 'version' in self._prolog[1].text.lower():
#			self._prolog[1].text = VERSION
#		else:
#			self._prolog.insert(1, _Comment(VERSION))
#		self._prolog[1].tail = '\n\n'

# lxml itertext returns text from all elements, includings comments, and
# ElementTree itertext won't return text from our CDATA and Entity elements,
# so we need to define our own function that will work correctly in all cases

def getalltext(elem):
	def itertext(elem):
		if isinstance(elem.tag, basestring) or elem.tag in (CDATA, Entity):
			if elem.text: yield elem.text
			for child in elem:
				for text in itertext(child): yield text
				if child.tail: yield child.tail
	return ''.join(itertext(elem))

def setalltext(elem, text):
	elem.text = text
	del elem[:]

class Document(object):

	def __init__(self, docitems=()):
		parser = XMLParser(strip_cdata=False)
		self._tree = ElementTree(parser=parser)
		self._clsmap = dict((cls._name_, cls) for cls in docitems)

	def load(self, source):
		self._tree.parse(source)
		if self._map and self._tree.docinfo.root_name not in self._map:
			raise ValueError('%r is not a supported document type' % self._tree.docinfo.root_name)

	def save(self, dest, encoding=None):
		if not encoding:
			encoding = self._tree.docinfo.encoding or 'UTF-8'
		_add_tstmp()
		self._tree.write(dest, encoding=encoding, pretty_print=True)

	@property
	def root(self):
		item = Item(self._tree.getroot())
		if self._clsmap:
			return self._clsmap[self._tree.docinfo.root_name](item)
		else:
			return item

class Item(object):

	__slots__ = ['_elem']

	def __init__(self, elem):
		self._elem = elem

	def __repr__(self):
		return repr(self._elem)

	def __getitem__(self, attr):
		if attr:
			return self._elem.get(attr) if self._elem is not None else None
		else:
			return getalltext(self._elem) if self._elem is not None else None

	def __setitem__(self, attr, value):
		if attr:
			self._elem.set(attr, value)
		else:
			setalltext(self._elem, CDATA(value) if '\n' in value else value)
#			self._elem.text = CDATA(value) if '\n' in value else value

	def __delitem__(self, attr):
		if attr:
			self._elem.attrib.pop(attr, None)
		else:
			self._elem.text = None

	def _iterchildren(self, tags):
		for child in self._elem:
			if child.tag in tags:
				yield Item(child)

	def _getchild(self, tag, create=False):
		child = self._elem.find(tag)
		if child is None and create:
			child = SubElement(self._elem, tag)
		return Item(child)

	def _delchild(self, child):
		self._elem.remove(child._elem)

	@property
	def name(self):
		return self._elem.tag if self._elem is not None else None

